Passed
Push — master ( 22c2e0...be3ba8 )
by Emil
03:07
created

auth.getNewAPIKey   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 23
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 3.0416

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 3
eloc 14
c 1
b 0
f 0
nc 3
nop 2
dl 0
loc 23
ccs 5
cts 6
cp 0.8333
crap 3.0416
rs 9.7
1 1
const db = require("../db/database.js");
2 1
const hat = require("hat");
3 1
const validator = require("email-validator");
4
5 1
const bcrypt = require('bcrypt');
6 1
const jwt = require('jsonwebtoken');
7
8
let config;
9
10 1
try {
11 1
    config = require('../config/config.json');
12
} catch (error) {
13 1
    console.error(error);
14
}
15
16 2
const jwtSecret = process.env.JWT_SECRET || config.secret;
17
18 1
const auth = {
19
    isValidAPIKey: function(apiKey, next, path, res) {
20 98
        db.get("SELECT email FROM apikeys WHERE key = ?", apiKey, (err, row) => {
21 98
            if (err) {
22
                return res.status(500).json({
23
                    errors: {
24
                        status: 500,
25
                        source: path,
26
                        title: "Database error",
27
                        detail: err.message
28
                    }
29
                });
30
            }
31
32 98
            if (row !== undefined) {
33 89
                return next();
34
            }
35
36 9
            return res.status(401).json({
37
                errors: {
38
                    status: 401,
39
                    source: path,
40
                    title: "Valid API key",
41
                    detail: "No valid API key provided."
42
                }
43
            });
44
        });
45
    },
46
47
    getNewAPIKey: function(res, path, email) {
48 11
        if (email === undefined || !validator.validate(email)) {
49 2
            return res.status(401).json({
50
                errors: {
51
                    status: 401,
52
                    source: path,
53
                    title: "Valid email",
54
                    detail: "A valid email address is required to obtain an API key."
55
                }
56
            });
57
        }
58
59 9
        db.get("SELECT email, key FROM apikeys WHERE email = ?", email, (err, row) => {
60 9
            if (err) {
61
                return res.status(500).json({
62
                    errors: {
63
                        status: 500,
64
                        source: path,
65
                        title: "Database error",
66
                        detail: err.message
67
                    }
68
                });
69
            }
70
71 9
            if (row !== undefined) {
72 1
                return res.json({
73
                    data: {
74
                        message: "Email address already used for api key.",
75
                        apiKey: row.key
76
                    }
77
                });
78
            }
79
80 8
            return auth.getUniqueAPIKey(res, path, email);
81
        });
0 ignored issues
show
Best Practice introduced by
There is no return statement in this branch, but you do return something in other branches. Did you maybe miss it? If you do not want to return anything, consider adding return undefined; explicitly.
Loading history...
82
    },
83
84
    getUniqueAPIKey: function(res, path, email) {
85 8
        const apiKey = hat();
86
87 8
        db.get("SELECT key FROM apikeys WHERE key = ?", apiKey, (err, row) => {
88 8
            if (err) {
89
                return res.status(401).json({
90
                    errors: {
91
                        status: 401,
92
                        source: path,
93
                        title: "Database error",
94
                        detail: err.message
95
                    }
96
                });
97
            }
98
99 8
            if (row === undefined) {
100 8
                db.run("INSERT INTO apikeys (key, email) VALUES (?, ?)",
101
                    apiKey,
102
                    email, (err) => {
103 8
                        if (err) {
104
                            return res.status(401).json({
105
                                errors: {
106
                                    status: 401,
107
                                    source: path,
108
                                    title: "Database error",
109
                                    detail: err.message
110
                                }
111
                            });
112
                        }
113
114 8
                        return res.json({ data: { key: apiKey }});
115
                    });
0 ignored issues
show
Best Practice introduced by
There is no return statement in this branch, but you do return something in other branches. Did you maybe miss it? If you do not want to return anything, consider adding return undefined; explicitly.
Loading history...
116
            } else {
117
                return auth.getUniqueAPIKey(res, email);
118
            }
119
        });
120
    },
121
122
    login: function(res, body) {
123 6
        const email = body.email;
124 6
        const password = body.password;
125 6
        const apiKey = body.api_key;
126
127 6
        if (!email || !password) {
128 2
            return res.status(401).json({
129
                errors: {
130
                    status: 401,
131
                    source: "/login",
132
                    title: "Email or password missing",
133
                    detail: "Email or password missing in request"
134
                }
135
            });
136
        }
137
138 4
        db.get("SELECT * FROM users WHERE apiKey = ? AND email = ?",
139
            apiKey,
140
            email,
141
            (err, rows) => {
142 4
                if (err) {
143
                    return res.status(500).json({
144
                        errors: {
145
                            status: 500,
146
                            source: "/login",
147
                            title: "Database error",
148
                            detail: err.message
149
                        }
150
                    });
151
                }
152
153 4
                if (rows === undefined) {
154 1
                    return res.status(401).json({
155
                        errors: {
156
                            status: 401,
157
                            source: "/login",
158
                            title: "User not found",
159
                            detail: "User with provided email not found."
160
                        }
161
                    });
162
                }
163
164 3
                const user = rows;
165
166 3
                bcrypt.compare(password, user.password, (err, result) => {
167 3
                    if (err) {
168
                        return res.status(500).json({
169
                            errors: {
170
                                status: 500,
171
                                source: "/login",
172
                                title: "bcrypt error",
173
                                detail: "bcrypt error"
174
                            }
175
                        });
176
                    }
177
178 3
                    if (result) {
179 2
                        let payload = { api_key: user.apiKey, email: user.email };
180 2
                        let jwtToken = jwt.sign(payload, jwtSecret, { expiresIn: '24h' });
181
182 2
                        return res.json({
183
                            data: {
184
                                type: "success",
185
                                message: "User logged in",
186
                                user: payload,
187
                                token: jwtToken
188
                            }
189
                        });
190
                    }
191
192 1
                    return res.status(401).json({
193
                        errors: {
194
                            status: 401,
195
                            source: "/login",
196
                            title: "Wrong password",
197
                            detail: "Password is incorrect."
198
                        }
199
                    });
200
                });
0 ignored issues
show
Best Practice introduced by
There is no return statement in this branch, but you do return something in other branches. Did you maybe miss it? If you do not want to return anything, consider adding return undefined; explicitly.
Loading history...
201
            });
0 ignored issues
show
Best Practice introduced by
There is no return statement in this branch, but you do return something in other branches. Did you maybe miss it? If you do not want to return anything, consider adding return undefined; explicitly.
Loading history...
202
    },
203
204
    register: function(res, body) {
205 5
        const email = body.email;
206 5
        const password = body.password;
207 5
        const apiKey = body.api_key;
208
209 5
        if (!email || !password) {
210 2
            return res.status(401).json({
211
                errors: {
212
                    status: 401,
213
                    source: "/register",
214
                    title: "Email or password missing",
215
                    detail: "Email or password missing in request"
216
                }
217
            });
218
        }
219
220 3
        bcrypt.hash(password, 10, function(err, hash) {
221 3
            if (err) {
222
                return res.status(500).json({
223
                    errors: {
224
                        status: 500,
225
                        source: "/register",
226
                        title: "bcrypt error",
227
                        detail: "bcrypt error"
228
                    }
229
                });
230
            }
231
232 3
            db.run("INSERT INTO users (apiKey, email, password) VALUES (?, ?, ?)",
233
                apiKey,
234
                email,
235
                hash, (err) => {
236 3
                    if (err) {
237 1
                        return res.status(500).json({
238
                            errors: {
239
                                status: 500,
240
                                source: "/register",
241
                                title: "Database error",
242
                                detail: err.message
243
                            }
244
                        });
245
                    }
246
247 2
                    return res.status(201).json({
248
                        data: {
249
                            message: "User successfully registered."
250
                        }
251
                    });
252
                });
0 ignored issues
show
Best Practice introduced by
There is no return statement in this branch, but you do return something in other branches. Did you maybe miss it? If you do not want to return anything, consider adding return undefined; explicitly.
Loading history...
253
        });
0 ignored issues
show
Best Practice introduced by
There is no return statement in this branch, but you do return something in other branches. Did you maybe miss it? If you do not want to return anything, consider adding return undefined; explicitly.
Loading history...
254
    },
255
256
    checkToken: function(req, res, next) {
257 14
        var token = req.headers['x-access-token'];
258
259 14
        if (token) {
260 11
            jwt.verify(token, jwtSecret, function(err, decoded) {
261 11
                if (err) {
262
                    return res.status(500).json({
263
                        errors: {
264
                            status: 500,
265
                            source: req.path,
266
                            title: "Failed authentication",
267
                            detail: err.message
268
                        }
269
                    });
270
                }
271
272 11
                req.user = {};
273 11
                req.user.api_key = decoded.api_key;
274 11
                req.user.email = decoded.email;
275
276 11
                next();
277
278 11
                return undefined;
279
            });
0 ignored issues
show
Best Practice introduced by
There is no return statement in this branch, but you do return something in other branches. Did you maybe miss it? If you do not want to return anything, consider adding return undefined; explicitly.
Loading history...
280
        } else {
281 3
            return res.status(401).json({
282
                errors: {
283
                    status: 401,
284
                    source: req.path,
285
                    title: "No token",
286
                    detail: "No token provided in request headers"
287
                }
288
            });
289
        }
290
    }
291
};
292
293
module.exports = auth;
294